package com.jackstraw.jack_base.datasource.elasticsearch.repository;

import co.elastic.clients.elasticsearch._types.SortOptions;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.BulkResponse;
import co.elastic.clients.elasticsearch.core.CountResponse;
import co.elastic.clients.elasticsearch.core.CreateResponse;
import co.elastic.clients.elasticsearch.core.GetResponse;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.UpdateResponse;
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse;
import co.elastic.clients.elasticsearch.indices.GetIndexResponse;
import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.elasticsearch.indices.IndexState;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import co.elastic.clients.util.ObjectBuilder;
import com.jackstraw.jack_base.datasource.elasticsearch.aspect.annotation.SearchFind;
import com.jackstraw.jack_base.datasource.elasticsearch.entity.bean.ElasticSearchRouting;
import com.jackstraw.jack_base.datasource.elasticsearch.entity.bean.ElasticSearchTemplate;
import com.jackstraw.jack_base.datasource.elasticsearch.entity.ElasticSearchBaseEntity;
import com.jackstraw.jack_base.datasource.elasticsearch.exception.BadElasticSearchException;
import com.jackstraw.jack_base.datasource.elasticsearch.util.ElasticSearchUtil;
import com.jackstraw.jack_base.util.CollectionUtil;
import com.jackstraw.jack_base.util.DateTimeUtil;
import com.jackstraw.jack_base.util.JsonUtil;
import com.jackstraw.jack_base.util.ObjectUtil;
import com.jackstraw.jack_base.util.SegmenterUtil;
import com.jackstraw.jack_base.util.StringUtil;
import com.jackstraw.jack_base.util.TypeUtil;
import com.jackstraw.jack_base.util.function.ReflectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.data.elasticsearch.annotations.Setting;
import org.springframework.lang.Nullable;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 索引的Repository操作抽象类
 *
 * @param <T>
 */
@Slf4j
public class ElasticsearchRepository<T extends ElasticSearchBaseEntity<T>>
        implements org.springframework.data.elasticsearch.repository.ElasticsearchRepository<T, String> {

    private ElasticSearchRouting elasticSearchRouting;


    private String indexName;

    private Class<T> entityClass;

    private Document document;

    private Setting setting;

    /**
     * 创建索引时的字段属性
     */
    private Map<String, Property> propertyMap;


    /**
     * 文档内属性字段
     */
    private List<Field> fields;

    /**
     * 字段类型
     */
    private Map<Field, FieldType> filedTypeMap;

    /**
     * 要被检索的字段
     * <p>
     * 该值默认为当前类有SearchFind注解
     */
    private List<Field> searchFindFields;

    /**
     * 要被检索的字段名称
     * <p>
     * 该值默认为当前类有SearchFind注解
     */
    private List<String> searchFindFiledNameList;


    /**
     * 要被检索的字段名称
     * <p>
     * 该值默认为当前类有SearchFind注解
     */
    private Map<FieldType, List<String>> searchFindFiledNameMap;


    public ElasticsearchRepository(ElasticSearchRouting elasticSearchRouting,
                                   Class<T> entityClass) {
        this.elasticSearchRouting = elasticSearchRouting;
        this.entityClass = entityClass;

        if (!entityClass.isAnnotationPresent(Document.class)) {
            throw new BadElasticSearchException("042", "反射获取文档注解[Document]异常");
        }
        Document document = entityClass.getAnnotation(Document.class);
        if (!entityClass.isAnnotationPresent(Setting.class)) {
            throw new BadElasticSearchException("043", "反射获取文档注解[Setting]异常");
        }
        Setting setting = entityClass.getAnnotation(Setting.class);
        this.document = document;
        this.setting = setting;
        this.indexName = document.indexName();
        if (StringUtil.StringUtilsBylang3.isEmpty(this.indexName)) {
            //当没有指定文档名称时，获取类名当索引名称
            this.indexName = entityClass.getName();
        }

    }


    public ElasticSearchRouting getElasticSearchRouting() {
        return elasticSearchRouting;
    }

    public String getIndexName() {
        return indexName;
    }

    public Class<T> getEntityClass() {
        return entityClass;
    }


    public ElasticSearchTemplate elasticSearchTemplate() {
        return elasticSearchRouting.determineTargetEntity();
    }

    public Map<Field, FieldType> getFiledTypeMap() {
        if (ObjectUtils.isEmpty(this.filedTypeMap)) {
            this.filedTypeMap = new HashMap<>();
        }
        if (this.filedTypeMap.size() > 0) {
            return this.filedTypeMap;
        }
        this.getFields().stream().forEach(field -> {
            org.springframework.data.elasticsearch.annotations.Field fieldAnnotation = field.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class);
            this.filedTypeMap.put(field, fieldAnnotation.type());
        });
        return this.filedTypeMap;
    }

    public Map<String, Property> getPropertyMap() {
        if (ObjectUtils.isEmpty(this.propertyMap)) {
            this.propertyMap = new HashMap<>();
        }
        if (this.propertyMap.size() > 0) {
            return this.propertyMap;
        }
        this.propertyMap.putAll(ElasticSearchUtil.property(this.getFields()));
        return this.propertyMap;
    }

    public List<Field> getFields() {
        if (ObjectUtils.isEmpty(this.fields)) {
            this.fields = new ArrayList<>();
        }
        if (this.fields.size() > 0) {
            return this.fields;
        }
        this.fields = ElasticSearchUtil.indexField(this.entityClass);
        return this.fields;
    }

    public List<Field> getSearchFindFields() {
        if (ObjectUtils.isEmpty(this.searchFindFields)) {
            this.searchFindFields = new ArrayList<>();
        }
        if (this.searchFindFields.size() > 0) {
            return this.searchFindFields;
        }
        searchFindFields.addAll(ReflectionUtil.getDeclaredFieldsAll(this.entityClass)
                .stream().filter(field
                        -> field.isAnnotationPresent(SearchFind.class) && field.isAnnotationPresent(org.springframework.data.elasticsearch.annotations.Field.class))
                .collect(Collectors.toList()));
        return this.searchFindFields;
    }

    /**
     * 自动匹配要被检索的名称
     *
     * @return
     */
    public List<String> getSearchFindFiledNameList() {
        if (ObjectUtils.isEmpty(this.searchFindFiledNameList)) {
            this.searchFindFiledNameList = new ArrayList<>();
        }
        if (this.searchFindFiledNameList.size() > 0) {
            return this.searchFindFiledNameList;
        }
        searchFindFiledNameList.addAll(this.getSearchFindFields()
                .stream().map(Field::getName)
                .collect(Collectors.toList()));
        return this.searchFindFiledNameList;
    }

    /**
     * ES字段类型对应的字段名称
     *
     * @return
     */
    public Map<FieldType, List<String>> getSearchFindFiledNameMap() {
        if (ObjectUtils.isEmpty(this.searchFindFiledNameMap)) {
            this.searchFindFiledNameMap = new HashMap<>();
        }
        if (this.searchFindFiledNameMap.size() > 0) {
            return this.searchFindFiledNameMap;
        }
        this.searchFindFiledNameMap.putAll(this.getSearchFindFields().stream()
                .collect(Collectors.groupingBy(field
                                -> field.getAnnotation(org.springframework.data.elasticsearch.annotations.Field.class).type()
                        , Collectors.mapping(Field::getName, Collectors.toList()))));
        return this.searchFindFiledNameMap;
    }

    /**
     * 创建索引
     */
    public Boolean createIndex() {
        try {

            IndexSettings.Builder indexSettingsBuilder = new IndexSettings.Builder();

            indexSettingsBuilder.numberOfReplicas(this.stringRepresentation(setting.replicas()))
                    .numberOfShards(this.stringRepresentation(setting.shards()))
                    .refreshInterval(Time.of(t -> t.time(setting.refreshInterval())));

            CreateIndexResponse createIndexResponse = this.elasticSearchTemplate().get()
                    .indices()
                    .create(c
                            -> c.index(indexName)
                            .mappings(m -> m.properties(this.getPropertyMap()))
                            .settings(indexSettingsBuilder.build()));
            return createIndexResponse.acknowledged();
        } catch (IOException e) {
            throw new BadElasticSearchException("024", "indexName为[{}]的文档创建索引异常", e, indexName);
        }
    }

    /**
     * 删除索引
     */
    public Boolean deleteIndex() {
        try {
            DeleteIndexResponse deleteIndexResponse = this.elasticSearchTemplate().get().indices().delete(c -> c.index(indexName));
            return deleteIndexResponse.acknowledged();
        } catch (IOException e) {
            throw new BadElasticSearchException("025", "indexName为[{}]的文档删除索引异常", e, indexName);
        }
    }

    public Boolean existsIndex() {
        try {
            BooleanResponse booleanResponse = this.elasticSearchTemplate().get().indices().exists(c -> c.index(indexName));
            return booleanResponse.value();
        } catch (IOException e) {
            throw new BadElasticSearchException("027", "indexName为[{}]的文档查询索引是否存在异常", e, indexName);
        }
    }

    /**
     * 查询索引详情
     *
     * @return
     */
    public Map<String, IndexState> queryIndex() {
        try {
            if (!this.existsIndex()) {
                throw new BadElasticSearchException("028", "indexName为[{}]的文档索引不存在，无法查询索引详情", indexName);
            }
            GetIndexResponse getIndexResponse = this.elasticSearchTemplate().get().indices().get(c -> c.index(indexName));
            return getIndexResponse.result();

        } catch (IOException e) {
            throw new BadElasticSearchException("026", "indexName为[{}]的文档查询索引详情异常", e, indexName);
        }
    }

    /**
     * 修改文档
     *
     * @param entity must not be {@literal null}.
     * @return
     */
    public T update(T entity) {
        try {
            if (ObjectUtil.ObjectUtilByLang3.isEmpty(entity)) {
                throw new BadElasticSearchException("029", "indexName为[{}]的文档修改时文档数据为空", indexName);
            }
            UpdateResponse<T> updateResponse = this.elasticSearchTemplate()
                    .get().update(c
                            -> c.index(indexName)
                            .id(entity.getId())
                            .doc(entity), entityClass);
            return entity;
        } catch (IOException e) {
            throw new BadElasticSearchException("030", "indexName为[{}]的文档修改异常", e, indexName);
        }
    }

    /**
     * 批量修改
     *
     * @param entities must not be {@literal null} nor must it contain {@literal null}.
     * @return
     */
    public Iterable<T> updateAll(Iterable<T> entities) {

        try {
            List<T> entityList = IterableUtils.toList(entities);

            if (CollectionUtil.isEmpty(entityList)) {
                throw new BadElasticSearchException("031", "indexName为[{}]的文档批量修改时文档数据为空", indexName);
            }
            List<BulkOperation> bulkOperationList = entityList.stream().map(entity -> {
                if (ObjectUtil.ObjectUtilByLang3.isEmpty(entityList)) {
                    throw new BadElasticSearchException("032", "indexName为[{}]的文档批量修改时存在数据为空的文档", indexName);
                }
                return new BulkOperation.Builder()
                        .update(d
                                -> d.index(indexName)
                                .id(entity.getId())
                                .action(a -> a.doc(entity))).build();
            }).collect(Collectors.toList());

            BulkResponse bulkResponse = this.elasticSearchTemplate()
                    .get().bulk(e
                            -> e.index(indexName)
                            .operations(bulkOperationList));
            if (bulkResponse.errors()) {
                throw new BadElasticSearchException("033", "indexName为[{}]的文档批量修改时异常", indexName);
            }

            return bulkResponse.items().stream().map(bulkResponseItem
                    -> JsonUtil.getJsonToBean(bulkResponseItem.result(), entityClass)).collect(Collectors.toList());
        } catch (IOException e) {
            throw new BadElasticSearchException("034", "indexName为[{}]的文档批量修改时异常", e, indexName);
        }
    }

    /**
     * 条件查询
     *
     * @param entity
     * @return
     */
    public List<T> search(T entity) {
        SearchRequest.Builder searchRequestBuilder = this.searchRequestBuilder(entity);
        try {
            SearchResponse<T> searchResponse = this.elasticSearchTemplate().get().search(searchRequestBuilder.build(), entityClass);
            return searchResponse.hits().hits().stream().map(h -> h.source()).collect(Collectors.toList());
        } catch (IOException e) {
            throw new BadElasticSearchException("039", "indexName为[{}]的文档查询时异常", e, indexName);
        }

    }

    /**
     * 通过ID查询文档
     *
     * @param id must not be {@literal null}.
     * @return
     */
    @Override
    public Optional<T> findById(String id) {
        try {
            GetResponse<T> getResponse = this.elasticSearchTemplate()
                    .get()
                    .get(e -> e.index(indexName).id(id)
                            , entityClass);
            return Optional.ofNullable(getResponse.source());
        } catch (IOException e) {
            throw new BadElasticSearchException("001", "indexName为[{}]的文档通过ID[{}]查找文档异常", e, indexName, id);
        }

    }


    /**
     * 查找所有文档
     *
     * @return
     */
    @Override
    public List<T> findAll() {
        try {
            SearchResponse<T> searchResponse = this.elasticSearchTemplate().get()
                    .search(s -> s.index(indexName)
                            .query(q -> q.matchAll(m -> m)), entityClass);
            return searchResponse.hits().hits().stream().map(h -> h.source()).collect(Collectors.toList());
        } catch (IOException e) {
            throw new BadElasticSearchException("003", "indexName为[{}]的文档全量查询异常", e, indexName);
        }
    }


    /**
     * 分页或排序查询所有文档
     *
     * @param pageable
     * @return
     */
    @Override
    public Page<T> findAll(Pageable pageable) {
        try {
            Function<SearchRequest.Builder, ObjectBuilder<SearchRequest>> fn = s -> s.index(indexName)
                    .query(q -> q.matchAll(m -> m))
                    .from(pageable.getPageNumber() * pageable.getPageSize())
                    .size(pageable.getPageSize());

            //判断是否含有排序
            if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(pageable.getSort())) {
                fn.apply(new SearchRequest.Builder()
                        .sort(this.sortOptions(pageable.getSort())
                                .collect(Collectors.toList())));
            }

            SearchResponse<T> searchResponse = this.elasticSearchTemplate().get().search(fn, entityClass);
            List<T> tList = searchResponse.hits().hits().stream().map(h -> h.source()).collect(Collectors.toList());
            return new PageImpl<>(tList, pageable, tList.size());
        } catch (IOException e) {
            throw new BadElasticSearchException("004", "indexName为[{}]的文档全量分页排序查询异常", e, indexName);
        }
    }

    /**
     * 排序查询所有文档
     *
     * @param sort
     * @return
     */
    @Override
    public List<T> findAll(Sort sort) {
        try {
            SearchResponse<T> searchResponse = this.elasticSearchTemplate().get()
                    .search(s -> s.index(indexName)
                            .query(q -> q.matchAll(m -> m))
                            .sort(this.sortOptions(sort).collect(Collectors.toList())), entityClass);
            return searchResponse.hits().hits().stream().map(h -> h.source()).collect(Collectors.toList());
        } catch (IOException e) {
            throw new BadElasticSearchException("005", "indexName为[{}]的文档全量排序查询异常", e, indexName);
        }
    }

    /**
     * 通过ID集合查询文档
     *
     * @param ids must not be {@literal null} nor contain any {@literal null} values.
     * @return
     */
    @Override
    public Iterable<T> findAllById(Iterable<String> ids) {
        try {
            //将ID转换为字符类型
            List<String> stringIdList = IterableUtils.toList(ids);
            SearchResponse<T> searchResponse = this.elasticSearchTemplate().get()
                    .search(s -> s.index(indexName).query(q -> q.ids(i -> i.values(stringIdList))),
                            entityClass);
            return searchResponse.hits().hits().stream().map(h -> h.source()).collect(Collectors.toList());
        } catch (IOException e) {
            throw new BadElasticSearchException("006", "indexName为[{}]的文档通过id的集合查询失败", e, indexName);
        }
    }

    /**
     * 统计整个文档的数量
     *
     * @return
     */
    public long count() {
        try {
            CountResponse countResponse = this.elasticSearchTemplate().get().count(c -> c.index(indexName));
            return countResponse.count();
        } catch (IOException e) {
            throw new BadElasticSearchException("002", "indexName为[{}]的文档统计数量异常", e, indexName);

        }
    }

    /**
     * 保存文档
     *
     * @param entity must not be {@literal null}.
     * @param <S>
     * @return
     */
    public <S extends T> S save(S entity) {
        try {
            if (ObjectUtil.ObjectUtilByLang3.isEmpty(entity)) {
                throw new BadElasticSearchException("007", "indexName为[{}]的文档保存时文档数据为空", indexName);
            }
            CreateResponse createResponse = this.elasticSearchTemplate().get()
                    .create(c
                            -> c.index(indexName)
                            .id(entity.getId())
                            .document(entity));
            return entity;
        } catch (IOException e) {
            throw new BadElasticSearchException("008", "indexName为[{}]的文档保存异常", e, indexName);
        }
    }

    /**
     * 批量保存
     *
     * @param entities must not be {@literal null} nor must it contain {@literal null}.
     * @param <S>
     * @return
     */
    @Override
    public <S extends T> Iterable<S> saveAll(Iterable<S> entities) {

        try {
            List<S> entityList = IterableUtils.toList(entities);

            if (CollectionUtil.isEmpty(entityList)) {
                throw new BadElasticSearchException("009", "indexName为[{}]的文档批量保存时文档数据为空", indexName);
            }
            List<BulkOperation> bulkOperationList = entityList.stream().map(entity -> {
                if (ObjectUtil.ObjectUtilByLang3.isEmpty(entityList)) {
                    throw new BadElasticSearchException("010", "indexName为[{}]的文档批量保存时存在数据为空的文档", indexName);
                }
                return new BulkOperation.Builder().create(d -> d.index(indexName).id(entity.getId()).document(entity)).build();
            }).collect(Collectors.toList());

            BulkResponse bulkResponse = this.elasticSearchTemplate().get()
                    .bulk(e -> e.index(indexName).operations(bulkOperationList));
            if (bulkResponse.errors()) {
                throw new BadElasticSearchException("011", "indexName为[{}]的文档批量保存时异常", indexName);
            }
            return entities;
        } catch (IOException e) {
            throw new BadElasticSearchException("023", "indexName为[{}]的文档批量保存时异常", e, indexName);
        }
    }

    /**
     * 判断文档中该id是否存在
     *
     * @param id must not be {@literal null}.
     * @return
     */
    @Override
    public boolean existsById(String id) {
        try {
            BooleanResponse booleanResponse = this.elasticSearchTemplate().get().exists(e -> e.index(indexName).id(id));
            return booleanResponse.value();
        } catch (IOException e) {
            throw new BadElasticSearchException("012", "indexName为[{}]的文档查询id[{}]是否存在异常", e, indexName, id);
        }
    }

    /**
     * 使用类似的查询搜索相似的实体
     *
     * @param entity   the entity for which similar documents should be searched, must not be {@literal null}
     * @param fields
     * @param pageable , must not be {@literal null}
     * @return
     */
    @Override
    public Page<T> searchSimilar(T entity, @Nullable String[] fields, Pageable pageable) {

        try {
            if (ObjectUtil.ObjectUtilByLang3.isEmpty(entity)) {
                throw new BadElasticSearchException("019", "indexName为[{}]的文档条件查询时条件为空", this.getIndexName());
            }
            if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(fields) && fields.length > 0) {
                entity.setIncludeFields(Arrays.asList(fields));
            }
            if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(pageable)) {
                entity.setPageSize(pageable.getPageSize());
                entity.setPageNum(pageable.getPageNumber());
                entity.setSort(pageable.getSort());
            }

            SearchRequest.Builder searchRequestBuilder = this.searchRequestBuilder(entity);
            SearchResponse<T> searchResponse = this.elasticSearchTemplate().get().search(searchRequestBuilder.build(), entityClass);
            List<T> tList = searchResponse.hits().hits().stream().map(h -> h.source()).collect(Collectors.toList());
            return new PageImpl<>(tList, pageable, tList.size());
        } catch (IOException e) {
            throw new BadElasticSearchException("016", "indexName为[{}]的文档条件查询异常", e, indexName);
        }

    }


    /**
     * 通过ID删除文档
     *
     * @param id must not be {@literal null}.
     */
    @Override
    public void deleteById(String id) {
        if (StringUtil.StringUtilsBylang3.isEmpty(id)) {
            throw new BadElasticSearchException("017", "indexName为[{}]的文档通过id删除时ID为空", indexName);
        }
        try {
            this.elasticSearchTemplate().get().delete(t -> t.index(indexName).id(id));
        } catch (IOException e) {
            throw new BadElasticSearchException("018", "indexName为[{}]的文档通过id[{}]删除时异常", e, indexName, id);
        }

    }

    /**
     * 通过条件删除文档
     *
     * @param entity must not be {@literal null}.
     */
    @Override
    public void delete(T entity) {
        Query.Builder queryBuilder = this.queryBuilder(entity);
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(queryBuilder)) {
            throw new BadElasticSearchException("020", "indexName为[{}]的文档通过条件删除时条件为空", indexName);
        }
        try {
            this.elasticSearchTemplate().get()
                    .deleteByQuery(d -> d.index(indexName)
                            .query(queryBuilder.build()));
        } catch (IOException e) {
            throw new BadElasticSearchException("020", "indexName为[{}]的文档通过条件删除时异常", e, indexName);
        }

    }

    /**
     * 通过ID集合删除文档
     *
     * @param ids must not be {@literal null}. Must not contain {@literal null} elements.
     */
    @Override
    public void deleteAllById(Iterable<? extends String> ids) {
        try {
            T entity = entityClass.newInstance();
            entity.setIds((List<String>) IterableUtils.toList(ids));
            this.delete(entity);
        } catch (Exception e) {
            throw new BadElasticSearchException("036", "indexName为[{}]的文档通过ID集合删除时异常", e, indexName);
        }

    }

    /**
     * 删除给定的实体。
     *
     * @param entities must not be {@literal null}. Must not contain {@literal null} elements.
     */
    @Override
    public void deleteAll(Iterable<? extends T> entities) {
        List<? extends T> entityList = IterableUtils.toList(entities);
        if (CollectionUtil.isEmpty(entityList)) {
            log.warn("indexName为[{}]的文档通过条件集合删除时，条件为空", indexName);
            return;
        }
        List<String> idList = entityList.stream().map(e -> e.getId()).collect(Collectors.toList());
        this.deleteAllById(idList);
    }


    /**
     * 无条件删除所有文档
     */
    @Override
    public void deleteAll() {
        try {
            this.elasticSearchTemplate().get().delete(d -> d.index(indexName));
        } catch (IOException e) {
            throw new BadElasticSearchException("035", "indexName为[{}]的文档全量删除异常", e, indexName);
        }
    }

    /**
     * sort转换为SortOptions流
     *
     * @param sort
     * @return
     */
    public Stream<SortOptions> sortOptions(Sort sort) {
        return sort.stream().map(order -> {
            Sort.Direction direction = order.getDirection();
            String property = order.getProperty();
            return new SortOptions.Builder().field(s
                            -> s.field(property)
                            .order(direction.isAscending() ? SortOrder.Asc : SortOrder.Desc))
                    .build();
        });
    }

    /**
     * 将object转换为字符类型
     *
     * @param object
     * @return
     */
    public @Nullable String stringRepresentation(@Nullable Object object) {
        return Objects.toString(object, null);
    }

    /**
     * 根据检索字段创建查询条件
     *
     * @param findStr
     * @return
     */
    public Query.Builder queryBuilder(String findStr) {
        if (StringUtil.StringUtilsBylang3.isEmpty(findStr)) {
            return null;
        }
        Map<FieldType, List<String>> filedNameMap = this.getSearchFindFiledNameMap();
        if (CollectionUtil.MapUtil.isEmpty(filedNameMap)) {
            return null;
        }

        //条件判断
        Boolean queryFlag = Boolean.FALSE;

        Query.Builder queryBuilder = new Query.Builder();
        List<String> textList = filedNameMap.get(FieldType.Text);
        if (CollectionUtil.isNotEmpty(textList)) {
            //Text需要分词进行检索
            Set<String> findStrSet = SegmenterUtil.ikSegment(findStr);
            queryFlag = Boolean.TRUE;
            findStrSet.stream()
                    .map(findStrSub -> "*".concat(findStrSub.trim().toLowerCase()).concat("*"))
                    .forEach(findStrSub -> {
                        textList.stream().forEach(filedName -> {
                            queryBuilder.wildcard(w -> w.field(filedName).wildcard(findStrSub).caseInsensitive(Boolean.TRUE));
                        });
                    });
        }

        List<String> keywordList = filedNameMap.get(FieldType.Keyword);
        if (CollectionUtil.isNotEmpty(keywordList)) {
            queryFlag = Boolean.TRUE;
            keywordList.stream().forEach(filedName -> {
                queryBuilder.term(m -> m.field(filedName).value(findStr.trim()));
            });
        }
        if (TypeUtil.isNumeric(findStr.trim())) {
            if (CollectionUtil.isNotEmpty(filedNameMap.get(FieldType.Double))) {
                queryFlag = Boolean.TRUE;
                filedNameMap.get(FieldType.Double).stream().forEach(filedName -> {
                    queryBuilder.term(m -> m.field(filedName).value(findStr.trim()));
                });
            }
            if (CollectionUtil.isNotEmpty(filedNameMap.get(FieldType.Integer))) {
                queryFlag = Boolean.TRUE;
                filedNameMap.get(FieldType.Integer).stream().forEach(filedName -> {
                    queryBuilder.term(m -> m.field(filedName).value(findStr.trim()));
                });
            }
            if (CollectionUtil.isNotEmpty(filedNameMap.get(FieldType.Long))) {
                queryFlag = Boolean.TRUE;
                filedNameMap.get(FieldType.Long).stream().forEach(filedName -> {
                    queryBuilder.term(m -> m.field(filedName).value(findStr.trim()));
                });
            }
            if (CollectionUtil.isNotEmpty(filedNameMap.get(FieldType.Float))) {
                queryFlag = Boolean.TRUE;
                filedNameMap.get(FieldType.Float).stream().forEach(filedName -> {
                    queryBuilder.term(m -> m.field(filedName).value(findStr.trim()));
                });
            }
            if (CollectionUtil.isNotEmpty(filedNameMap.get(FieldType.Short))) {
                queryFlag = Boolean.TRUE;
                filedNameMap.get(FieldType.Short).stream().forEach(filedName -> {
                    queryBuilder.term(m -> m.field(filedName).value(findStr.trim()));
                });
            }
        }

        if (TypeUtil.isDate(findStr.trim())) {
            if (CollectionUtil.isNotEmpty(filedNameMap.get(FieldType.Date))) {
                queryFlag = Boolean.TRUE;
                filedNameMap.get(FieldType.Date).stream().forEach(filedName -> {
                    queryBuilder.term(m -> m.field(filedName).value(findStr.trim()));
                });
            }
        }


        return queryFlag ? queryBuilder : null;
    }

    /**
     * 构建BoolQuery.Builder
     *
     * @param entity
     * @return
     */
    public BoolQuery.Builder boolQueryBuilder(T entity, BoolQuery.Builder boolQueryBuilder) {
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(entity)) {
            return null;
        }
        //条件判断
        AtomicReference<Boolean> queryFlag = new AtomicReference<>(Boolean.FALSE);
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(boolQueryBuilder)) {
            boolQueryBuilder = new BoolQuery.Builder();
        }
        BoolQuery.Builder finalBoolQueryBuilder = boolQueryBuilder;
        this.getFields().stream().forEach(field -> {
            field.setAccessible(Boolean.TRUE);
            try {
                Object fieldValue = field.get(entity);
                if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(fieldValue)) {
                    FieldType fieldType = this.getFiledTypeMap().get(field);
                    switch (fieldType) {
                        case Text:
                            queryFlag.set(Boolean.TRUE);
                            finalBoolQueryBuilder.must(m
                                    -> m.match(mv
                                    -> mv.field(field.getName())
                                    .query((String) fieldValue)));
                            break;
                        case Keyword:
                            queryFlag.set(Boolean.TRUE);
                            finalBoolQueryBuilder.must(m
                                    -> m.term(t
                                    -> t.field(field.getName())
                                    .value((String) fieldValue)));
                            break;
                        case Long:
                        case Integer:
                        case Short:
                            queryFlag.set(Boolean.TRUE);
                            finalBoolQueryBuilder.must(m
                                    -> m.term(t
                                    -> t.field(field.getName())
                                    .value((Long) fieldValue)));
                            break;
                        case Double:
                        case Float:
                            queryFlag.set(Boolean.TRUE);
                            finalBoolQueryBuilder.must(m
                                    -> m.term(t
                                    -> t.field(field.getName())
                                    .value((Double) fieldValue)));
                            break;
                        case Boolean:
                            queryFlag.set(Boolean.TRUE);
                            finalBoolQueryBuilder.must(m
                                    -> m.term(t
                                    -> t.field(field.getName())
                                    .value((Boolean) fieldValue)));
                            break;
                        case Date:
                            queryFlag.set(Boolean.TRUE);
                            finalBoolQueryBuilder.must(m
                                    -> m.term(t
                                    -> t.field(field.getName())
                                    .value(DateTimeUtil.format((Date) fieldValue, DateTimeUtil.NORM_DATETIME_MS_PATTERN))));
                            break;
                        case Nested:

                            break;
                    }
                }
            } catch (IllegalAccessException e) {
                throw new BadElasticSearchException("019", "indexName为[{}]的文档条件查询时构建默认查询字段查询异常", e, this.getIndexName());
            }
        });

        if (CollectionUtil.isNotEmpty(entity.getIds())) {
            queryFlag.set(Boolean.TRUE);
            boolQueryBuilder.must(m -> m.ids(i -> i.values(entity.getIds())));
        }
        if (StringUtil.StringUtilsBylang3.isNotEmpty(entity.getFindStr())) {
            Query.Builder findStrQueryBuilder = this.queryBuilder(entity.getFindStr());
            if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(findStrQueryBuilder)) {
                queryFlag.set(Boolean.TRUE);
                boolQueryBuilder.must(findStrQueryBuilder.build());
            }
        }
        return queryFlag.get() ? boolQueryBuilder : null;
    }

    /**
     * 构建BoolQuery.Builder
     *
     * @param entity
     * @return
     */
    public Query.Builder queryBuilder(T entity) {
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(entity)) {
            throw new BadElasticSearchException("019", "indexName为[{}]的文档条件查询时条件为空", this.getIndexName());
        }

        Query.Builder queryBuilder = new Query.Builder();

        BoolQuery.Builder boolQueryBuilder = this.boolQueryBuilder(entity, null);
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(boolQueryBuilder)) {
            return null;
        }
        queryBuilder.bool(boolQueryBuilder.build());

        return queryBuilder;
    }

    /**
     * 根据条件创建带有分页和排序SearchRequest
     *
     * @param entity
     * @return
     */
    public SearchRequest.Builder searchRequestBuilder(T entity) {
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        searchRequestBuilder.index(indexName);
        Query.Builder queryBuilder = this.queryBuilder(entity);
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(queryBuilder)) {
            searchRequestBuilder.query(queryBuilder.build());
        }

        //设置查询数量（不使用全量查询，防止性能消耗）
        if (ObjectUtil.ObjectUtilByLang3.isEmpty(entity.getPageSize())) {
            //如果没有设置，那就用配置文件内设置的默认的
            entity.setPageSize(this.elasticSearchTemplate().getQuerySize());
        }
        searchRequestBuilder.from(entity.getStartIndex()).size(entity.getPageSize());

        //设置排序
        if (ObjectUtil.ObjectUtilByLang3.isNotEmpty(entity.getSort())) {
            searchRequestBuilder.sort(this.sortOptions(entity.getSort()).collect(Collectors.toList()));
        }

        //设置查询字段或排除字段
        if (CollectionUtil.isNotEmpty(entity.getIncludeFields())) {
            searchRequestBuilder.source(s -> s.filter(f -> f.includes(entity.getIncludeFields())));
        }
        if (CollectionUtil.isNotEmpty(entity.getExcludeFields())) {
            searchRequestBuilder.source(s -> s.filter(f -> f.includes(entity.getExcludeFields())));
        }


        return searchRequestBuilder;
    }

}
